home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Graphics 2D / Restore Screen Cluts / WindowPositioner.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-09-28  |  22.9 KB  |  546 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        WindowPositioner.c
  3.  
  4.     Contains:    Main program file for the ColorReset application
  5.  
  6.     Written by: Forrest Tanaka    
  7.  
  8.     Copyright:    Copyright © 1988-1999 by Apple Computer, Inc., All Rights Reserved.
  9.  
  10.                 You may incorporate this Apple sample source code into your program(s) without
  11.                 restriction. This Apple sample source code has been provided "AS IS" and the
  12.                 responsibility for its operation is yours. You are not permitted to redistribute
  13.                 this Apple sample source code as "Apple sample source code" after having made
  14.                 changes. If you're going to re-distribute the source, we require that you make
  15.                 it clear in the source that the code was descended from Apple sample source
  16.                 code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.                 7/13/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  20.                 
  21.  
  22. */
  23.  
  24. /******************************************************************************\
  25. * Header Files
  26. \******************************************************************************/
  27.  
  28. #ifndef THINK_C
  29. #include <Memory.h>
  30. #include <Script.h>
  31. #endif
  32.  
  33. #include <GestaltEqu.h>
  34. #include "WindowPositioner.h"
  35.  
  36.  
  37. /******************************************************************************\
  38. * Constants
  39. \******************************************************************************/
  40.  
  41. #define kAlertFactor 3 /* Denom of fraction of screen area above alert */
  42.  
  43. /* Collision rectangle specifications */
  44. #define kCollInitH  0  /* Initial offset of collision rect from base rect */
  45. #define kCollInitV  0  /* Initial offset of collision rect from base rect */
  46. #define kCollWidth  16 /* Width of collision rectangle in pixels */
  47. #define kCollHeight 16 /* Height of collision rectangle in pixels */
  48.  
  49. /* Staggered-window wrapping options */
  50. #define kNotWrapped  0 /* Collision testing hasn’t wrapped yet */
  51. #define kHorzWrapped 1 /* Collision testing wrapped horizontally */
  52. #define kVertWrapped 2 /* Collision testing wrapped vertically */
  53.  
  54. /* Coordinates of temporary off-screen window for calculating bias */
  55. #define kOffScreenTop    -32008 /* Top coordinate of off-screen window */
  56. #define kOffScreenLeft   -32008 /* Left coordinate of off-screen window */
  57. #define kOffScreenBottom -32000 /* Bottom coordinate of off-screen window */
  58. #define kOffScreenRight  -32000 /* Right coordinate of off-screen window */
  59.  
  60. #ifndef THINK_C
  61. #define topLeft(r) (*((Point *) &(r).top))
  62. #define botRight(r) (*((Point *) &(r).bottom))
  63. #define screenBits qd.screenBits
  64. #endif
  65.  
  66.  
  67. /******************************************************************************\
  68. * Prototypes
  69. \******************************************************************************/
  70.  
  71. void StaggerScreenRect(
  72.     Rect  *baseRect,
  73.     Rect  *modWindowRect,
  74.     short hBias,
  75.     short vBias);
  76.  
  77. GDHandle GetWindowGDevice(
  78.     WindowPtr theWindow);
  79.  
  80. void GetGlobalWindowRect(
  81.     WindowPtr theWindow,
  82.     Rect      *globalRect);
  83.  
  84.  
  85. /******************************************************************************\
  86. * Public: PositionScreenRect
  87. *
  88. * PositionScreenRect concerns itself with two rectangles: the base rectangle and
  89. * the window rectangle.  The window rectangle is simply the rectangle of the
  90. * window as passed in the windowRect parameter.  The base rectangle is what the
  91. * window rectangle should be positioned on.  If the screenOption specifies that
  92. * the window rectangle should be positioned based on a screen, then the base
  93. * rectangle is the global rectangle of the screen that the window should be
  94. * displayed on.  If the screenOption specifies that the window rectangle should
  95. * be positioned relative to the position of another window, then the base
  96. * rectangle is the global coordinates of the portRect of that window.
  97. *
  98. * Two screen options (specified by the screenOption parameter) specify that a
  99. * window should be based on one screen or another.  Multiple screens are only
  100. * supported on Color QuickDraw machines, so PositionScreenRect starts off by
  101. * eliminating the parentScreenPos option on black & white QuickDraw machines by
  102. * changing the kParentScreenPos option to the kMainScreenPos option.  Some
  103. * companies offer multiple screens for black & white QuickDraw machines, but
  104. * there’s no standard way to get at the rectangles of those other screens.
  105. *
  106. * The base rectangle is calculated first.  If the screen option is
  107. * kMainScreenPos, then the base rectangle can be copied from the screenBits.
  108. * bounds QuickDraw global.  If the screen option is kParentPos, then the base
  109. * rectangle is a copy of the portRect of the parent window (specified by the
  110. * parentWindow parameter) offset so that it has global coordinates.  If the
  111. * screen option is kParentScreenPos, then the base rectangle is the rectangle of
  112. * the screen that the parent window has most of its area on.  If the screen
  113. * option isn’t any of these, then the point (0,0) is returned and nothing more
  114. * is done.
  115. *
  116. * After the base rectangle is calculated, then the position of the window on
  117. * that base rectangle is calculated.  If the position option is kCenterPos, then
  118. * the window rectangle is centered right smack in the middle of the base
  119. * rectangle.  If the position option is kAlertPos, then the window rectangle is
  120. * centered horizontally, but it’s higher than the center vertically.  The amount
  121. * higher that it should be is specified by the kAlertFactor constant.  This
  122. * constant specifies the proportion of the vertical screen space aside from the
  123. * space taken up by the window should appear below the window.  For now,
  124. * kAlertFactor is 3, which means that there should be three times more screen
  125. * space below the window than above.
  126. \******************************************************************************/
  127.  
  128. void PositionScreenRect(
  129.     Rect      *windowRect,    /* Rectangle to center */
  130.     short     screenOption,   /* Options for screen to center against */
  131.     short     positionOption, /* Centering options */
  132.     WindowPtr parentWindow,   /* Pointer to parent window, if any */
  133.     short     hBias,          /* portRect.left-strucRgn.rgnBBox.left */
  134.     short     vBias)          /* portRect.top-strucRgn.rgnBBox.top */
  135. {
  136.     long     qdVersion;        /* Version of QuickDraw in use */
  137.     Rect     baseRect;         /* Rectangle to center against */
  138.     GDHandle maxGDevice;       /* GDevice containing most of parentWindow */
  139.     short    vCenterFactor;    /* ÷ left-over screen by this to get v coord */
  140.     Point    upperLeft;        /* Returns position of portRect.topLeft */
  141.     Boolean  goodScreenOption; /* True if screen option is valid */
  142.     Boolean  hasCQD;           /* True if Color QuickDraw is available */
  143.  
  144.     /* Determine whether Color QuickDraw is available or not */
  145.     (void)Gestalt( gestaltQuickdrawVersion, /*<*/&qdVersion );
  146.     hasCQD = qdVersion >= gestalt8BitQD;
  147.  
  148.     /* If want parent window’s screen but no CQD, then assume main screen */
  149.     if (screenOption == kParentScreenPos && !hasCQD)
  150.         screenOption = kMainScreenPos;
  151.  
  152.     /* If parent window is nil but it’s needed, then assume main screen */
  153.     if ((screenOption == kParentPos || screenOption == kParentScreenPos) &&
  154.             parentWindow == nil)
  155.         screenOption = kMainScreenPos;
  156.  
  157.     /* Assume the specified screen option is good */
  158.     goodScreenOption = true;
  159.  
  160.     /* Find the base rectangle */
  161.     if (screenOption == kMainScreenPos)
  162.     {
  163.         /* Base rectangle is the main screen */
  164.         baseRect = screenBits.bounds;
  165.         baseRect.top += GetMBarHeight();
  166.     }
  167.     else if (screenOption == kParentPos)
  168.         /* Base rectangle is the parent window’s portRect */
  169.         GetGlobalWindowRect( parentWindow, /*<*/&baseRect );
  170.     else if (screenOption == kParentScreenPos)
  171.     {
  172.         /* Base rectangle is screen containing most of parent window */
  173.         maxGDevice = GetWindowGDevice( parentWindow );
  174.         baseRect = (**maxGDevice).gdRect;
  175.  
  176.         /* If window’s GDevice is main screen’s, then take menu bar out */
  177.         if (maxGDevice == GetMainDevice())
  178.             baseRect.top += GetMBarHeight();
  179.     }
  180.     else
  181.     {
  182.         /* Invalid screen option */
  183.         goodScreenOption = false;
  184.     }
  185.  
  186.     /* Calculate a proper location for the window if screen option is valid */
  187.     if (goodScreenOption)
  188.     {
  189.         if (positionOption == kStaggerPos)
  190.         {
  191.             StaggerScreenRect( &baseRect, /*◊*/windowRect, hBias, vBias );
  192.             if (windowRect->right > baseRect.right - (kCollWidth / 2))
  193.                 windowRect->right = baseRect.right - (kCollWidth / 2);
  194.             if (windowRect->bottom > baseRect.bottom - (kCollHeight / 2))
  195.                 windowRect->bottom = baseRect.bottom - (kCollHeight / 2);
  196.         }
  197.         else if (positionOption == kCenterPos || positionOption == kAlertPos)
  198.         {
  199.             /* Find amount to divide vertical screen area left over */
  200.             if (positionOption == kCenterPos)
  201.                 vCenterFactor = 2;
  202.             else if (positionOption == kAlertPos)
  203.                 vCenterFactor = kAlertFactor;
  204.  
  205.             /* Calc left and top coordinates of destination rectangle */
  206.             upperLeft.h = (((baseRect.right - baseRect.left) - (windowRect->
  207.                     right - windowRect->left)) >> 1) + baseRect.left;
  208.             upperLeft.v = (((baseRect.bottom - baseRect.top) - (windowRect->
  209.                     bottom - windowRect->top)) / vCenterFactor) + baseRect.top;
  210.  
  211.             /* Offset the window rectangle to upperLeft */
  212.             OffsetRect( /*◊*/windowRect, upperLeft.h - windowRect->left,
  213.                     upperLeft.v - windowRect->top );
  214.         }
  215.     }
  216. }
  217.  
  218.  
  219. /******************************************************************************\
  220. * Private: StaggerScreenRect - Find staggered position for a window
  221. *
  222. * This routine returns a point that, if applied to the top-left corner of the
  223. * rectangle specified by windowRect, puts windowRect into staggered position
  224. * within the rectangle specified by baseRect.  baseRect, windowRect, and the
  225. * returned point are all assumed to be in global (screen) coordinates.
  226. *
  227. * “Staggered position” means that an attempt is made to position windowRect such
  228. * that its top-left corner isn’t close to the top-left corner of an existing
  229. * window’s frame.  To do this, a “collision rectangle” located near the top-left
  230. * corner of baseRect is created and placed into the collRect local variable.
  231. * The window list is searched to see if the top-left corner of any window frames
  232. * are in the collision rectangle, which would constitute a collision.
  233. * Initially, no collisions are tolerated.  If even one colliding window is
  234. * found, then the collision rectangle is moved down and to the right and the
  235. * window list is searched for collisions again.  If a collision is again found,
  236. * then the collision rectangle is moved down and to the right again and so
  237. * forth.
  238. *
  239. * A working copy of windowRect (StaggerScreenRect doesn’t modify windowRect)
  240. * in the local variable, workWindowRect, follows collRect such that it’s top-
  241. * left corner is at the center of collRect.  As collRect is offset after a
  242. * collision is found, workWindowRect is offset by the same amount.
  243. *
  244. * If any part of workWindowRect falls outside of baseRect, then collRect and
  245. * workWindowRect wrap around to the side of baseRect opposite to the side that
  246. * workWindowRect fell off of, and collision-testing resumes there.  This
  247. * wrapping is only allowed in one direction though.  For example, if
  248. * workWindowRect falls off of the right edge of baseRect, then it and collRect
  249. * wrap around to the left side of baseRect at the same vertical position.  If
  250. * the bottom edge of workWindowRect subsequently falls off of the bottom edge of
  251. * baseRect, then it won’t wrap around to the top because horizontally wrapping
  252. * has already happened.  Instead, collRect and workWindowRect are set back into
  253. * their initial positions at the top-left corner of baseRect.  If instead,
  254. * workWindowRect first falls off of the bottom edge of baseRect, then it and
  255. * collRect wrap around to the top of baseRect at the same horizontal position.
  256. * If the right edge of workWindowRect subsequently falls off of the right edge
  257. * of baseRect, then it won’t wrap around to the left because vertical wrapping
  258. * has already happened.  Instead, collRect and workWindowRect are again set back
  259. * into their initial positions at the top-left corner of baseRect.
  260. *
  261. * Of course, setting collRect and workWindowRect back to their initial positions
  262. * guarantees a collision.  So, StaggerScreenRect becomes more tolerant of
  263. * collisions.  After finding a collision at every attempt, it now allows one
  264. * collision before moving on to the next position.  If every position had more
  265. * than one collision, then StaggerScreenRect becomes even more tolerant and
  266. * allows up to two collisions.  This process continues until a suitable position
  267. * for workWindowRect is found.
  268. *
  269. * In all truth, workWindowRect isn’t an EXACT copy of windowRect.  The value of
  270. * the hBias parameter is added to both horizontal coordinates and the value of
  271. * vBias is added to both vertical coordinates.  hBias should hold the number of
  272. * pixels between the top coordinate of the portRect of the window being
  273. * positioned and the top coordinate of the window’s frame.  Similarly, vBias
  274. * should hold the number of pixels between the left coorindate of the portRect
  275. * and the left cooordinate of the window’s frame.
  276. \******************************************************************************/
  277.  
  278. static void StaggerScreenRect(
  279.     Rect  *baseRect,      /* Rectangle within which to stagger window rect */
  280.     Rect  *modWindowRect, /* Port rectangle to be staggered within baseRect */
  281.     short hBias,          /* portRect.left-strucRgn.rgnBBox.left */
  282.     short vBias)          /* portRect.top-strucRgn.rgnBBox.top */
  283. {
  284.     WindowPtr testWindow;       /* Window being tested to see if it collides */
  285.     Rect      testWindowRect;   /* testWindow’s window frame rectangle */
  286.     Rect      collRect;         /* Rectangle in which to test for collisions */
  287.     Rect      initCollRect;     /* Initial collRect at upper left of baseRect */
  288.     Rect      workWindowRect;   /* Working copy of modWindowRect */
  289.     short     windowRectWidth;  /* Width of modWindowRect */
  290.     short     windowRectHeight; /* Height of modWindowRect */
  291.     short     collCount;        /* # collisions found so far within collRect */
  292.     short     maxCollCount;     /* Maximum allowed collisions within collRect */
  293.     short     wrapStatus;       /* Tells how collRect has wrapped, if at all */
  294.     Boolean   foundSlot;        /* True if found good slot for modWindowRect */
  295.  
  296.     /* Set up the initial collision rectangle at offset from baseRect */
  297.     initCollRect.top = baseRect->top + kCollInitV;
  298.     initCollRect.left = baseRect->left + kCollInitH;
  299.     initCollRect.bottom = initCollRect.top + kCollHeight;
  300.     initCollRect.right = initCollRect.left + kCollWidth;
  301.  
  302.     /* Set up the working destination rectangle */
  303.     windowRectWidth = modWindowRect->right - modWindowRect->left;
  304.     windowRectHeight = modWindowRect->bottom - modWindowRect->top;
  305.     workWindowRect.top = initCollRect.top + vBias + kCollHeight / 2;
  306.     workWindowRect.left = initCollRect.left + hBias + kCollWidth / 2;
  307.     workWindowRect.bottom = workWindowRect.top + windowRectHeight;
  308.     workWindowRect.right = workWindowRect.left + windowRectWidth;
  309.  
  310.     /* Set up initial conditions for the search */
  311.     collRect = initCollRect;
  312.     maxCollCount = 0;
  313.     wrapStatus = kNotWrapped;
  314.     foundSlot = false;
  315.  
  316.     /* Search for a slot until an appropriate one is found */
  317.     while (!foundSlot)
  318.     {
  319.         /* See if a slot has <= maximum number of allowed collisions */
  320.         collCount = 0;
  321.         testWindow = FrontWindow();
  322.         while (testWindow != nil && collCount <= maxCollCount)
  323.         {
  324.             /* Get the global rectangle covering entire window frame */
  325.             testWindowRect = (**((WindowPeek)testWindow)->strucRgn).rgnBBox;
  326.  
  327.             /* If top left of window frame in testWindowRect, then collision */
  328.             if (PtInRect( topLeft( testWindowRect ), &collRect ))
  329.                 collCount++;
  330.  
  331.             /* Go to the next window */
  332.             testWindow = (WindowPtr)((WindowPeek) testWindow)->nextWindow;
  333.         }
  334.  
  335.         /* If too many collisions, then shift collision rect to the next slot */
  336.         if (collCount > maxCollCount)
  337.         {
  338.             /* Shift collision rectangle to the next slot */
  339.             OffsetRect( /*◊*/&collRect, kCollWidth / 2, kCollHeight / 2 );
  340.             OffsetRect( /*◊*/&workWindowRect, kCollWidth / 2, kCollHeight / 2 );
  341.             if (workWindowRect.bottom > baseRect->bottom)
  342.             {
  343.                 if (wrapStatus != kHorzWrapped)
  344.                 {
  345.                     /* Wrap collision rect vertically */
  346.                     collRect.top = initCollRect.top;
  347.                     collRect.bottom = initCollRect.bottom;
  348.                     wrapStatus = kVertWrapped;
  349.                 }
  350.                 else
  351.                 {
  352.                     /* Wrapped horz, try from start & allow 1 more collision */
  353.                     collRect = initCollRect;
  354.                     maxCollCount++;
  355.                 }
  356.  
  357.                 /* Make workWindowRect follow collRect */
  358.                 workWindowRect.top = collRect.top + vBias + kCollHeight / 2;
  359.                 workWindowRect.left = collRect.left + hBias + kCollWidth / 2;
  360.                 workWindowRect.bottom = workWindowRect.top + windowRectHeight;
  361.                 workWindowRect.right = workWindowRect.left + windowRectWidth;
  362.             }
  363.             if (workWindowRect.right > baseRect->right)
  364.             {
  365.                 if (wrapStatus != kVertWrapped)
  366.                 {
  367.                     /* Wrap collision rect horizontally */
  368.                     collRect.left = initCollRect.left;
  369.                     collRect.right = initCollRect.right;
  370.                     wrapStatus = kHorzWrapped;
  371.                 }
  372.                 else
  373.                 {
  374.                     /* Wrapped vert, try from start & allow 1 more collision */
  375.                     collRect = initCollRect;
  376.                     maxCollCount++;
  377.                 }
  378.  
  379.                 /* Make workWindowRect follow collRect */
  380.                 workWindowRect.top = collRect.top + vBias + kCollHeight / 2;
  381.                 workWindowRect.left = collRect.left + hBias + kCollWidth / 2;
  382.                 workWindowRect.bottom = workWindowRect.top + windowRectHeight;
  383.                 workWindowRect.right = workWindowRect.left + windowRectWidth;
  384.             }
  385.         }
  386.         else
  387.             foundSlot = true;
  388.     }
  389.  
  390.     /* Return the top-left corner of workWindowRect */
  391.     OffsetRect( /*◊*/modWindowRect, workWindowRect.left - modWindowRect->left,
  392.             workWindowRect.top - modWindowRect->top );
  393. }
  394.  
  395.  
  396. /******************************************************************************\
  397. * Private: GetWindowGDevice - Get GDevice that contains most of a window
  398. *
  399. * This routine searches through all active screen GDevices in the GDevice list
  400. * for the GDevice of the screen that has the greatest area of intersection with
  401. * the portRect of the window specified by theWindow.  A handle to this GDevice
  402. * is returned.  If the portRect of theWindow is doesn’t intersect any active
  403. * screen, then nil is returned.
  404. *
  405. * This routine can only be called if Color QuickDraw is implemented because
  406. * the Graphics Device Manager only exists on Color QuickDraw machines.
  407. \******************************************************************************/
  408.  
  409. static GDHandle GetWindowGDevice(
  410.     WindowPtr theWindow) /* Pointer to window being tested */
  411. {
  412.     GDHandle testGDevice; /* Handle to GDevice being tested */
  413.     GDHandle maxGDevice;  /* GDevice with maximum intersection area */
  414.     Rect     windowRect;  /* Rect of window’s portRect in global coords */
  415.     Rect     commonRect;  /* Rect of intersection between windowRect & gdRect */
  416.     long     maxArea;     /* Max area of GDevice/portRect intersection found */
  417.     long     commonArea;  /* Area of intersection between windowRect & gdRect */
  418.  
  419.     /* gdRects in global coords, so get window’s portRect in global coords */
  420.     GetGlobalWindowRect( theWindow, /*<*/&windowRect );
  421.  
  422.     /* Loop through all active screen GDevices */
  423.     testGDevice = GetDeviceList();
  424.     maxArea = 0;
  425.     maxGDevice = nil;
  426.     while (testGDevice != nil)
  427.         {
  428.         /* Only test if GDevice is active screen GDevice */
  429.         if (TestDeviceAttribute( testGDevice, screenDevice ) &&
  430.                 TestDeviceAttribute ( testGDevice, screenActive ))
  431.             /* Only check area if window and gdRect intersect */
  432.             if (SectRect( &(**testGDevice).gdRect, &windowRect,
  433.                     /*<*/&commonRect ))
  434.             {
  435.                 /* Find area common to GDevice.gdRect and window’s portRect */
  436.                 commonArea = (long)(commonRect.right - commonRect.left) *
  437.                         (commonRect.bottom - commonRect.top);
  438.  
  439.                 /* If area > max area found, then update maxArea & maxGDevice */
  440.                 if (commonArea > maxArea)
  441.                 {
  442.                     maxArea = commonArea;
  443.                     maxGDevice = testGDevice;
  444.                 }
  445.             }
  446.  
  447.         /* Try the next GDevice */
  448.         testGDevice = GetNextDevice( testGDevice );
  449.     }
  450.  
  451.     return maxGDevice;
  452. }
  453.  
  454.  
  455. /******************************************************************************\
  456. * Private: GetGlobalWindowRect - Get a window’s portRect in global coordinates
  457. *
  458. * The portRect of the window specified by theWindow is converted from the
  459. * window’s local coordinates to global (screen) coordinates.  This converted
  460. * rectangle is returned in globalRect.  If theWindow is nil, then globalRect
  461. * returns the rectangle [T:0 L:0 B:0 R:0].
  462. \******************************************************************************/
  463.  
  464. static void GetGlobalWindowRect(
  465.     WindowPtr theWindow,   /* Pointer to window whose global rect we want */
  466.     Rect      *globalRect) /* Returns theWindow’s portRect in global coords */
  467. {
  468.     GrafPtr savedPort; /* Pointer to current GrafPort; for restoring */
  469.  
  470.     if (theWindow != nil)
  471.     {
  472.         GetPort( /*<*/&savedPort );
  473.         SetPort( theWindow );
  474.         *globalRect = theWindow->portRect;
  475.         LocalToGlobal( /*◊*/&topLeft( *globalRect ) );
  476.         LocalToGlobal( /*◊*/&botRight( *globalRect ) );
  477.         SetPort( savedPort );
  478.     }
  479.     else
  480.         SetRect( /*<*/globalRect, 0, 0, 0, 0 );
  481. }
  482.  
  483. /******************************************************************************\
  484. * Public: CalcWindowBias
  485. *
  486. * The only reliable way to calculate the bias of a window is actually to create
  487. * a visible window and measure the resulting thickness of the window frame, and
  488. * then to dispose of the window immediately after the measurement is taken.  To
  489. * avoid visual threats against good taste, the window is created far outside of
  490. * any possible screen position, and it’s created behind any other windows to
  491. * avoid deactivating existing windows.
  492. *
  493. * To avoid as much heap disruption as possible, the window record is pre-
  494. * allocated as a handle, which is then locked.  Then, NewWindow is called to
  495. * create the ephemeral window.  The bias is then calculated by subtracting the
  496. * left and top coordinates of the structure region from the left and top
  497. * coordinates of the content region.
  498. \******************************************************************************/
  499.  
  500. Point CalcWindowBias(
  501.     short   procID,     /* Defproc ID of window whose bias is being calculated */
  502.     Boolean goAwayFlag) /* True if window has a go-away box */
  503. {
  504.     Point      bias;        /* Calculated bias */
  505.     WindowPeek biasWindow;  /* Pointer to temp window used for bias calc */
  506.     Handle     windowStore; /* Handle to window record storage (kept locked) */
  507.     Rect       windowRect;  /* Rectangle of window in global coords */
  508.     GrafPtr    savedPort;   /* Pointer to current GrafPort for restoring */
  509.  
  510.     /* In case an error happens, default to zero bias */
  511.     bias.h = bias.v = 0;
  512.  
  513.     /* Allocate WindowRecord as relocatable to avoid cluttering heap */
  514.     windowStore = NewHandle( sizeof (WindowRecord) );
  515.     if (windowStore != nil)
  516.     {
  517.         GetPort( /*<*/&savedPort );
  518.  
  519.         /* WindowRecord must be locked */
  520.         HLock( windowStore );
  521.  
  522.         /* Calc rectangle that’s almost certain to be off any screen */
  523.         SetRect( /*<*/&windowRect, kOffScreenLeft, kOffScreenTop,
  524.                 kOffScreenRight, kOffScreenBottom );
  525.  
  526.         /* Create visible temporary window "behind" all existing windows */
  527.         biasWindow = (WindowPeek)NewWindow( *windowStore, &windowRect, "\p",
  528.                 true, procID, nil, goAwayFlag, 0L );
  529.         if (biasWindow != nil)
  530.         {
  531.             /* Bias is content region top-left - structure region top-left */
  532.             bias.h = (**biasWindow->contRgn).rgnBBox.left - (**biasWindow->
  533.                     strucRgn).rgnBBox.left;
  534.             bias.v = (**biasWindow->contRgn).rgnBBox.top - (**biasWindow->
  535.                     strucRgn).rgnBBox.top;
  536.         }
  537.  
  538.         /* Pretend this never happened */
  539.         SetPort( savedPort );
  540.         CloseWindow( (WindowPtr)biasWindow );
  541.         DisposeHandle( windowStore );
  542.     }
  543.  
  544.     return bias;
  545. }
  546.